<!DOCTYPE html>
<html lang="en">
  <head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover"/>
<meta name="description" content="Tutorial for GraphObject manipulation with GoJS."/> 
<link rel="stylesheet" href="../assets/css/style.css"/> 
<!-- Copyright 1998-2022 by Northwoods Software Corporation. -->    <title> GraphObject Manipulation </title>
    <link rel="stylesheet" href="../assets/css/prism.css"  />
    
    <script src="../release/go.js"></script>
  </head>
  <body>
  <nav id="navTop" class="w-full z-30 top-0 text-white bg-nwoods-primary">
    <div class="w-full container max-w-screen-lg mx-auto flex flex-wrap sm:flex-nowrap items-center justify-between mt-0 py-2">
      <div class="md:pl-4">
        <a class="text-white hover:text-white no-underline hover:no-underline
        font-bold text-2xl lg:text-4xl rounded-lg hover:bg-nwoods-secondary " href="../">
          <h1 class="mb-0 p-1 ">GoJS</h1>
        </a>
      </div>
      <button id="topnavButton" class="rounded-lg sm:hidden focus:outline-none focus:ring" aria-label="Navigation">
        <svg fill="currentColor" viewBox="0 0 20 20" class="w-6 h-6">
          <path id="topnavOpen" fill-rule="evenodd" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM9 15a1 1 0 011-1h6a1 1 0 110 2h-6a1 1 0 01-1-1z" clip-rule="evenodd"></path>
          <path id="topnavClosed" class="hidden" fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path>
        </svg>
      </button>
      <div id="topnavList" class="hidden sm:block items-center w-auto mt-0 text-white p-0 z-20">
        <ul class="list-reset list-none font-semibold flex justify-end flex-wrap sm:flex-nowrap items-center px-0 pb-0">
          <li class="p-1 sm:p-0"><a class="topnav-link" href="../learn/">Learn</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="../samples/">Samples</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="../intro/">Intro</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="../api/">API</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/products/register.html">Register</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="../download.html">Download</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="https://forum.nwoods.com/c/gojs/11">Forum</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/contact.html"
           target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/contact.html', 'contact');">Contact</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/sales/index.html"
           target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/sales/index.html', 'buy');">Buy</a></li>
        </ul>
      </div>
    </div>
    <hr class="border-b border-gray-600 opacity-50 my-0 py-0" />
  </nav>
    
    <div class="md:flex flex-col md:flex-row md:min-h-screen w-full max-w-screen-xl mx-auto">
      
    <div id="navSide" class="flex flex-col w-full md:w-40 lg:w-48 text-gray-700 bg-white flex-shrink-0">
      <div class="flex-shrink-0 px-8 py-4">
        <button id="navButton" class="rounded-lg md:hidden focus:outline-none focus:ring" aria-label="Navigation">
          <svg fill="currentColor" viewBox="0 0 20 20" class="w-6 h-6">
            <path id="navOpen" fill-rule="evenodd" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM9 15a1 1 0 011-1h6a1 1 0 110 2h-6a1 1 0 01-1-1z" clip-rule="evenodd"></path>
            <path id="navClosed" class="hidden" fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path>
          </svg>
        </button>
      </div>
      <nav id="navList" class="min-h-screen hidden md:block sidebar-nav flex-grow px-1 lg:px-4 pb-4 md:pb-0 md:overflow-y-auto break-words">
  <a href="../learn/index.html">Get Started</a>
  <a href="../learn/graphObject.html">Manipulating GraphObjects</a>
  <a href="../learn/interactivity.html">Interacting with Diagrams</a>
      </nav>
    </div>      
      <div class="pt-4 px-2 md:px-0 lg:px-4 pb-16 w-full overflow-hidden">

<h1>GraphObject Manipulation</h1>

<h2 id="ProgrammaticallyInteractiveWithNodes">
  Programmatically interacting with Nodes
</h2>

<p>
  This guide will show you some basic ways of programmatically interacting
  with <b>GoJS</b> nodes and links and model data. Throughout this page, we
  will use the following diagram setup as our starting point:
</p>

<pre class="lang-js"><code>
var $ = go.GraphObject.make;

var myDiagram =
  $(go.Diagram, "myDiagramDiv",
    { "undoManager.isEnabled": true });

// define a simple Node template
myDiagram.nodeTemplate =
  $(go.Node, "Auto",
    $(go.Shape, "Rectangle",
      // don't draw any outline
      { stroke: null },
      // the Shape.fill comes from the Node.data.color property
      new go.Binding("fill", "color")),
    $(go.TextBlock,
      // leave some space around larger-than-normal text
      { margin: 6, font: "18px sans-serif" },
      // the TextBlock.text comes from the Node.data.key property
      new go.Binding("text"))
  );

myDiagram.model = new go.GraphLinksModel(
  [
    { key: 1, text: "Alpha", color: "lightblue" },
    { key: 2, text: "Beta", color: "orange" },
    { key: 3, text: "Gamma", color: "lightgreen" },
    { key: 4, text: "Delta", color: "pink" }
  ]);
</code></pre>

<p>The code produces this Diagram:</p>

<!-- LIVE -->
<div id="myDiagramDiv1" class="diagramStyling" style="width: 700px; height: 150px"></div>
<script>
  function setupDiagram(divname) {
    var $ = go.GraphObject.make;

    var myDiagram =
      $(go.Diagram, divname,
        { "undoManager.isEnabled": true });

    myDiagram.nodeTemplate =
      $(go.Node, "Auto",
        $(go.Shape, "Rectangle",
          { stroke: null, name: 'SHAPE' },
          new go.Binding("fill", "color")),
        $(go.TextBlock,
          { margin: 6, font: "18px sans-serif" },
          new go.Binding("text"))
      );

    myDiagram.model = new go.GraphLinksModel(
      [
        { key: 1, text: "Alpha", color: "lightblue" },
        { key: 2, text: "Beta", color: "orange" },
        { key: 3, text: "Gamma", color: "lightgreen" },
        { key: 4, text: "Delta", color: "pink" }
      ]);

    return myDiagram;
  }
  setupDiagram("myDiagramDiv1");
</script>

<h2 id="FindingSingleNodesDiagramFindNodeForKey">
  Finding single nodes: Diagram.findNodeForKey
</h2>

<p>
  You can use <code>Diagram.findNodeForKey(key)</code> to get a reference to a
  Node in JavaScript. Key values in <b>GoJS</b> can be either strings or
  numbers. You can then use the Node reference to inspect and manipulate the
  Node.
</p>

<pre class="lang-js"><code>
var node = myDiagram.findNodeForKey(1);

// Selects the node programmatically, rather than clicking interactively:
myDiagram.select(node);

// Outputs a JavaScript object in the developer console window.
// The format of what is output will differ per browser, but is essentially the object:
// { key: "Alpha", color: "lightblue" }
// plus some internal implementation details.
console.log(node.data);
</code></pre>

<!-- LIVE -->
<div id="myDiagramDiv2" class="diagramStyling" style="width: 700px; height: 150px"></div>
<script>
  var myDiagram = setupDiagram("myDiagramDiv2");
  var node = myDiagram.findNodeForKey(1);
  myDiagram.select(node);
  if (window.console) console.log(node.data);
</script>

<p>
  However <code>findNodeForKey</code> may return <code>null</code> if no node
  data uses that key value. Also, it only looks at the model data to find a
  node data that uses the given key value, from which it finds the
  corresponding Node in the Diagram. It does not look at the text values of
  any TextBlocks that are within the Nodes, so it can work even if no text is
  shown at all.  And it does not look at any programmatically added Nodes that
  were not created from model data.
</p>
<p>
  Once you have a <code>Node</code>, you can get its key either via the
  <code>Node.key</code> property or by looking at its data:
  <code>someNode.data.key</code>, just as you can look at any of the data
  properties.
</p>

<h2 id="CollectionsOfNodesAndLinks">Collections of Nodes and Links</h2>

<p>
  Diagrams have several properties and methods that return iterators
  describing collections of Parts. Both Nodes and Links are kinds of Parts.
  <code>Diagram.nodes</code> and <code>Diagram.links</code> return iterators
  of all Nodes and Links in the Diagram, respectively.
  <code>Diagram.selection</code> returns an iterator of selected Parts (both
  selected Nodes and selected Links).
</p>

<p>
  There are also more specific methods for common operations, such as
  <code>Diagram.findTreeRoots()</code>
  which returns an iterator of all top-level Nodes that have no parent nodes.
</p>

<p>
  This next example uses <code>Diagram.nodes</code> and shows how to iterate
  over the collection.
</p>

<pre class="lang-js"><code>
// Calling Diagram.commit executes the given function between startTransaction and commitTransaction
// calls.  That automatically updates the display and allows the effects to be undone.
myDiagram.commit(function(d) {  // d === myDiagram

  // iterate over all nodes in Diagram
  d.nodes.each(function(node) {
    if (node.data.text === "Beta") continue; //skip Beta, just to contrast
    node.scale = 0.4; // shrink each node
  });

}, "decrease scale");
</code></pre>

<p>As a result we have very scaled-down nodes, except for Beta:</p>

<!-- LIVE -->
<div id="myDiagramDiv3" class="diagramStyling" style="width: 700px; height: 150px"></div>
<script>
  var myDiagram = setupDiagram("myDiagramDiv3");
  myDiagram.commit(function (d) {  // d === myDiagram

    // iterate over all nodes in Diagram
    d.nodes.each(function (node) {
      if (node.data.text === "Beta") return; //skip Beta, just to contrast
      node.scale = 0.4; // shrink each node
    });

  }, "decrease scale");
</script>

<h2 id="NamedGraphObjectsAndPanelFindObject">
  Named GraphObjects and Panel.findObject
</h2>

<p>
  Often we want to manipulate a property that belongs to one of the Node's
  elements, perhaps an element arbitrarily deep in the template. In our
  example Diagram, each Node has one Shape, and if we want to change the color
  of this Shape directly we would need a reference to it. To make it possible
  to find, we can give that Shape a name:
</p>

<pre class="lang-js"><code>
myDiagram.nodeTemplate =
  $(go.Node, "Auto",
    $(go.Shape, "Rectangle",
      { stroke: null, name: "SHAPE" }, // added the name property
      new go.Binding("fill", "color")),
    $(go.TextBlock,
      { margin: 6, font: "18px sans-serif" },
      new go.Binding("text"))
  );
</code></pre>

<p>
  Names allow us to easily find GraphObjects inside of Panels (all Nodes are
  also Panels) using <code>Panel.findObject</code>, which will search the
  visual tree of a Panel starting at that panel. So when we have a reference
  to a Node, we can call <code>someNode.findObject("SomeName")</code> to
  search through the node for a GraphObject with that name.
  It will return a reference to the named GraphObject if it is found,
  or <code>null</code> otherwise.
</p>

<p>
  Using this, we could make an HTML button that changes the fill of the Shape
  inside of a selected Node:
</p>

<pre class="lang-js"><code>
var selectionButton = document.getElementById("selectionButton");
selectionButton.addEventListener("click", function() {
  myDiagram.commit(function(d) {
    d.selection.each(function(node) {
      var shape = node.findObject("SHAPE");
      // If there was a GraphObject in the node named SHAPE, then set its fill to red:
      if (shape !== null) {
        shape.fill = "red";
      }
    });
  }, "change color");
});
</code></pre>

<!-- LIVE -->
<div id="myDiagramDiv4" class="diagramStyling" style="width: 700px; height: 150px"></div>
<button id="selectionButton">
  Select some Nodes, then click here to change their Shape.fill
</button>
<script>
  var myDiagram4 = setupDiagram("myDiagramDiv4");
  var selectionButton = document.getElementById("selectionButton");
  selectionButton.addEventListener("click", function () {
    myDiagram4.commit(function (d) {
      d.selection.each(function (node) {
        var shape = node.findObject("SHAPE");
        // If there was a GraphObject in the node named SHAPE, then set its fill to red:
        if (shape !== null) {
          shape.fill = "red";
        }
      });
    }, "change color");
  });
</script>

<h2 id="ChangingProperteisAndUpdatingModelUsingDataBindings">
  Changing Properties and Updating the Model using Data Bindings
</h2>

<p>
  Looking again at our Node template, we have the <code>Shape.fill</code>
  property data-bound to the "color" property of our Node data:
</p>

<pre class="lang-js"><code>
myDiagram.nodeTemplate =
  $(go.Node, "Auto",
    $(go.Shape, "Rectangle",
      { stroke: null, name: "SHAPE" },
      new go.Binding("fill", "color")),  // note this data binding
    $(go.TextBlock,
      { margin: 6, font: "18px sans-serif" },
      new go.Binding("text"))
  );
</code></pre>

<p>
  Changing the Shape's <code>fill</code> property inside our node will not, as
  the Node template currently stands, update the model data.
</p>

<pre class="lang-js"><code>
var node = myDiagram.findNodeForKey(1);
var shape = node.findObject("SHAPE");
shape.fill = "red";

// outputs "lightblue" - the model has not changed!
console.log(node.data.color);
</code></pre>

<p>
  This is undesirable in some cases. When we want the change to persist after
  saving and loading, we will want the model data updated too.
</p>

<p>
  However, in some situations this lack of persistence might be a good thing.
  For instance if we want the color change for only cosmetic purposes, such as
  changing the color of a button when hovering over it with the mouse, we
  would not want to modify the model data that might be saved.
</p>

<p>
  For now, suppose that we do want to update the model. The preferred way to
  do this is to modify the data in the model and depend on the data binding to
  automatically update the Shape. However, we cannot modify the data directly
  by just setting the JavaScript property.
</p>

<pre class="lang-js" style="border: 6px solid red"><code>
var node = myDiagram.findNodeForKey(1);

// DO NOT DO THIS!
// This would update the data, but GoJS would not be notified
// that this arbitrary JavaScript object has been modified,
// and the associated Node will not be updated appropriately
node.data.color = "red";
</code></pre>
<p>
  Instead we should set the data property using the method
  <code>Model.set(data, propertyName, propertyValue)</code>.
  As always, changes should always be performed within a transaction.
  There should only be a single transaction for all changes that
  need to be made at the same time, from the user's point of view.
  A convenient way to perform a transaction is to call <code>Model.commit</code>.
</p>

<pre class="lang-js" style="border: 6px solid lime"><code>
var node = myDiagram.findNodeForKey(1);

// Model.commit executes the given function within a transaction
myDiagram.model.commit(function(m) {  // m == the Model

  // This is the safe way to change model data.
  // GoJS will be notified that the data has changed
  // so that it can update the node in the Diagram
  // and record the change in the UndoManager.
  m.set(node.data, "color", "red");

}, "changed color");

// outputs "red" - the model has changed!
console.log(node.data.color);
// and the user will see the red node
</code></pre>

<div id="myDiagramDiv5" class="diagramStyling" style="width: 700px; height: 150px"></div>
<script>
  var myDiagram = setupDiagram("myDiagramDiv5");
  var node = myDiagram.findNodeForKey(1);
  // Model.commit executes the given function within a transaction
  myDiagram.model.commit(function (m) {
    // m == the Model
    // This is the safe way to change model data.
    // GoJS will be notified that the data has changed
    // so that it can update the node in the Diagram
    // and record the change in the UndoManager.
    m.set(node.data, "color", "red");
  }, "changed color");
</script>

<p>
  Note that there is no longer any need to name the Shape "SHAPE", because
  there is no longer any need to call <code>findObject</code> to look for the
  particular Shape. Data binding will automatically update properties, so we
  do not have to do that ourselves.
</p>

<pre class="lang-js"><code>
myDiagram.nodeTemplate =
  $(go.Node, "Auto",
    $(go.Shape, "Rectangle",
      { stroke: null },  // removed the name property
      new go.Binding("fill", "color")),
    $(go.TextBlock,
      { margin: 6, font: "18px sans-serif" },
      new go.Binding("text"))
  );
</code></pre>

<h2 id="SubjectMentionedAndFurtherReading">
  Subjects Mentioned and Further Reading
</h2>
<ul>
  <li>
    Data Binding — See the
    <a href="../intro/dataBinding.html">intro page on Data Binding</a> for lots more detail.
  </li>
  <li>
    Transactions — See the
    <a href="../intro/transactions.html">intro page on Transactions</a>.
  </li>
  <li>
    <code>console.log</code> — A powerful debugging aid that is part of most
    browser's developer tools. See the
    <a href="https://developers.google.com/chrome-developer-tools/docs/console">Chrome guide</a>
    or the
    <a href="https://developer.mozilla.org/en-US/docs/Tools/Browser_Console">Firefox guide</a>
    for their respective developer consoles. See more suggestions for
    debugging GoJS diagrams at the
    <a href="../intro/debugging.html">intro page on Debugging</a>.
  </li>
</ul>
<p>
  You may want to read more tutorials, such as the
  <a href="index.html">Learn GoJS</a> tutorial. and the
  <a href="interactivity.html">Interactivity</a> tutorial. You can also watch
  tutorials on
  <a href="https://www.youtube.com/channel/UC9We8EoX596-6XFjJDtZIDg">YouTube</a>.
</p>
<p>
  If you are ready for a comprehensive overview of <b>GoJS</b>, have a look at
  the <a href="../intro/index.html">technical introduction</a>. If you want to
  explore by example, have a look at
  <a href="../samples/index.html">the samples</a> to get a feel for what's
  possible with <b>GoJS</b>.
</p>

      </div>
    </div>
  
  <div class="bg-nwoods-primary">
    <section class="max-w-screen-lg text-white container mx-auto py-2 px-12">
      <p id="version" class="leading-none mb-2 my-4">GoJS</p>
    </section>
  </div><footer class="bg-nwoods-primary text-white">
  <div class="container max-w-screen-lg mx-auto  px-8">
    <div class="w-full py-6">

        <div class="max-w-screen-lg xl:max-w-screen-xl mx-auto px-4 sm:px-6 md:px-8">
          <ul class="text-sm font-medium pb-14 sm:pb-20 grid grid-cols-1 sm:grid-cols-3 gap-y-10">
            <li class="list-none row-span-2">
              <h2 class="text-base font-semibold tracking-wide">GoJS</h2>
              <ul class="list-none space-y-4 md:space-y-1 px-0">
                <li>
                  <a href="../samples/index.html">Samples</a>
                </li>
                <li>
                  <a href="../learn/index.html">Learn</a>
                </li>
                <li>
                  <a href="../intro/index.html">Intro</a>
                </li>
                <li>
                  <a href="../api/index.html">API</a>
                </li>
                <li>
                  <a href="../changelog.html">Changelog</a>
                </li>
                <li>
                  <a href="https://github.com/NorthwoodsSoftware/GoJS">GitHub</a>
                </li>
              </ul>
            </li>
            <li class="list-none row-span-2">
              <h2 class="text-base font-semibold tracking-wide">Support</h2>
              <ul class="list-none space-y-4 md:space-y-1 px-0">
                <li>
                  <a href="https://www.nwoods.com/contact.html"
                  target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/contact.html', 'contact');">Contact</a>
                </li>
                <li>
                  <a href="https://forum.nwoods.com/c/gojs">Forum</a>
                </li>
                <li>
                  <a href="https://www.nwoods.com/app/activate.aspx?sku=gojs">Activate</a>
                </li>
                <li>
                  <a href="https://www.nwoods.com/sales/index.html"
                  target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/sales/index.html', 'buy');">Buy</a>
                </li>
                <li>
                  <a href="https://www.youtube.com/channel/UC9We8EoX596-6XFjJDtZIDg">Videos</a>
                </li>
              </ul>
            </li>
            <li class="list-none row-span-2">
              <h2 class="text-base font-semibold tracking-wide">Company</h2>
              <ul class="list-none space-y-4 md:space-y-1 px-0">
                <li>
                  <a href="https://www.nwoods.com">Northwoods</a>
                </li>
                <li>
                  <a href="https://www.nwoods.com/about.html">About Us</a>
                </li>
                <li>
                  <a href="https://www.nwoods.com/contact.html">Contact Us</a>
                </li>
                <li>
                  <a href="https://twitter.com/northwoodsgo">Twitter</a>
                </li>

              </ul>
            </li>
          </ul>


      <p class="text-sm text-gray-100 md:mb-6">
        Copyright 1998-2022 <a class="text-white" href="https://www.nwoods.com">Northwoods Software</a>
      </p>
    </div>
  </div>
</footer>  </body>

<script async src="https://www.googletagmanager.com/gtag/js?id=UA-1506307-5"></script> 
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date()); gtag('config', 'UA-1506307-5');
  var getOutboundLink = function(url, label) {
    gtag('event', 'click', {
      'event_category': 'outbound',
      'event_label': label,
      'transport_type': 'beacon'
    });
  }

  // topnav
  var topButton = document.getElementById("topnavButton");
  var topnavList = document.getElementById("topnavList");
  topButton.addEventListener("click", function() {
    this.classList.toggle("active");
    topnavList.classList.toggle("hidden");
    document.getElementById("topnavOpen").classList.toggle("hidden");
    document.getElementById("topnavClosed").classList.toggle("hidden");
  });
</script>
  <script src="../assets/js/prism.js"></script>
  <script src="../assets/js/goDoc.js"></script>
  <script>
    document.addEventListener("DOMContentLoaded", function() {
      if (window.go) document.getElementById('version').textContent = "GoJS version " + go.version;
      if (window.goDoc) window.goDoc();
    });
  </script>
</html>